
#pragma once

#include <cstdint>
#include <data_basic.hpp>

#include "../ioxx_include_device.hpp"
#include "../ioxx_irq.hpp"
#include "./wire_interface.hpp"

namespace twixx {

    /**
     * @brief 用于单向传输OLED 数据的硬件I2C 实现
     *
     * 注意：必须修改Hal 库头文件，给I2C_HandleTypeDef 结构体里添加一个void * 指针PtrToCallbackObject。
     * 否则在Hal 库的回调函数里找不到要给哪个对象反馈事件，除非添加一堆全局变量
     *
     * @tparam N            缓冲区大小，双缓冲区总大小要乘两倍，比如N == 64，则双缓冲总共要占用128 字节
     * @tparam UseDMA       默认为中断传输，不使用DMA。UseDMA == true 时改用HAL 库的DMA 传输函数，要保证DMA 已经配置好
     * @tparam HandleType   HAL 库Handle 结构体类型
     */
    template <size_t N, bool UseDMA = false, typename HandleType = I2C_HandleTypeDef>
    class HalDoubleBufferedWireWriter : public IWireWriter {
       private:
        using DoubleBufferType = data_basic::DoubleBuffer<uint8_t, N>;
        DoubleBufferType _buf;

        HandleType *_handle;

        uint_fast8_t _slave_address;

        bool _tx_ed = false;

        bool _pending_start_signal = false;

        volatile bool _last_frame_met_error = false;

        /**
         * @brief 从handle 获取this 指针
         *
         * @param this_handle
         * @return HalDoubleBufferedWireWriter*
         */
        static HalDoubleBufferedWireWriter *_this_ptr(HandleType *this_handle) {
            return const_cast<HalDoubleBufferedWireWriter *>(reinterpret_cast<volatile HalDoubleBufferedWireWriter *>(this_handle->PtrToCallbackObject));
        }

        /**
         * @brief 检查I2C handle 是否正被其他任务使用
         *
         * @return true
         * @return false
         */
        bool _is_handle_occupied() {
            return _handle->PtrToCallbackObject != nullptr;
        }

        /**
         * @brief 将回调函数添加到I2C_HandleTypeDef 对象，标记I2C 外设被占用
         *
         * @return true   成功注册回调，并获取外设控制权
         * @return false  外设正被占用
         */
        bool _take_handle() {
            // 用这些lambda 作为回调函数。捕获列表必须为空，否则不能作为函数指针使用，
            // 所以lambda 里不能直接操作this 指针，必须从handle 里获取。
            //
            // WARNING: 注意，这些回调是从I2C 中断调用的
            auto master_tx_complete_callback = [](HandleType *this_handle) {
                auto t = _this_ptr(this_handle);

                // 发送完成时，终止缓冲区读取，允许swap
                t->_buf.end_read();
                t->_last_frame_met_error = false;
            };

            auto error_callback = [](HandleType *this_handle) {
                auto t = _this_ptr(this_handle);

                // TODO: 发生错误时
                t->_buf.end_read();
                t->_last_frame_met_error = true;
            };

            // 检查并注册回调，要进入临界区
            do {
                irqxx::CriticalScope cs;

                // TODO: 检查I2C busy 状态

                if (_is_handle_occupied()) {
                    return false;
                }
                _handle->PtrToCallbackObject = reinterpret_cast<void *>(this);
            } while (0);

            _handle->MasterTxCpltCallback = master_tx_complete_callback;
            _handle->ErrorCallback = error_callback;
            return true;
        }

        /**
         * @brief 释放I2C 外设的控制权
         *
         */
        void _release_handle() {
            _handle->PtrToCallbackObject = nullptr;
            _handle->MasterTxCpltCallback = nullptr;
            _handle->ErrorCallback = nullptr;
        }

       public:
        static constexpr size_t BufferSize = DoubleBufferType::BufferSize;

        HalDoubleBufferedWireWriter(HandleType &handle) :
            _handle(&handle) {}


        /**
         * @brief 准备写入，发送起始信号，选中指定从机
         *
         * @param slave_addr 采用与Wire 库相同的从机地址格式，7 位地址右对齐
         *
         * @return uint8_fast8_t
         */
        uint_fast8_t begin_tx(uint_fast8_t slave_addr) {
            _slave_address = slave_addr << 1;  // HAL 库要求I2C 地址左对齐
            _pending_start_signal = true;
            _buf.begin_write();
            return 0;
        }


        /**
         * @brief 执行缓冲区swap，开始异步传输
         *
         * 对end 多余的调用不会产生错误
         *
         * @return uint8_fast8_t
         */
        uint_fast8_t end() {
            // 如果没开始传输，或者没有写入数据，调用end 不会发生任何事
            if (!_pending_start_signal) {
                return wire_status::not_started;
            }

            _buf.end_write();
            auto wrote_count = _buf.write_index();
            if (wrote_count == 0) {
                return 0;
            }

            // TODO: 处理上一帧传输错误的情况
            // 默认应该是忽略，除非自定义个错误回调函数，不然就没办法处理好这种异步状态
            // _last_frame_met_error

            auto status = wire_status::ok;
            do {
                bool s = _buf.try_swap();
                if (!s) {
                    status = wire_status::busy;
                }
                else {
                    status = wire_status::ok;
                }
            } while (status != wire_status::ok);

            auto buf_ptr = _buf.begin_read();

            do {
                auto ret = HAL_OK;
                if constexpr (UseDMA) {
                    // 如果不存在DMA，也就没有这个函数，UseDMA == true 时会链接错误
                    extern void HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef * hi2c, uint16_t DevAddress, const uint8_t *pData, uint16_t Size);
                    ret = HAL_I2C_Master_Transmit_DMA(_handle, _slave_address, const_cast<uint8_t *>(buf_ptr), wrote_count);
                }
                else {
                    ret = HAL_I2C_Master_Transmit_IT(_handle, _slave_address, const_cast<uint8_t *>(buf_ptr), wrote_count);
                }

                if (ret != HAL_OK) {
                    status = wire_status::busy;
                }
                else {
                    status = wire_status::ok;
                }
            } while (status == wire_status::busy);

            _tx_ed = false;
            _pending_start_signal = false;
            return status;
        }


        /**
         * @brief 写1 字节
         *
         * @param b
         * @return uint_fast8_t
         */
        uint_fast8_t tx(uint_fast8_t b) {
            if (!_pending_start_signal) {
                return wire_status::not_started;
            }

            _tx_ed = true;

            if (_buf.not_full()) {
                _buf.write(b);
                return 0;
            }
            else {
                return wire_status::buffer_overflow;
            }
        }


        /**
         * @brief 准备开启数据传输，获取I2C 外设控制权，注册回调函数
         *
         *
         * @return true
         * @return false   I2C 被占用
         */
        bool take() {
            return _take_handle();
        }


        /**
         * @brief 终止传输，并释放I2C 外设的控制权
         *
         * @return true
         * @return false  传输未完成，
         */
        bool release() {
            if (!done()) {
                return false;
            }
            else {
                _release_handle();
                _buf.end_write();
                return true;
            }
        }


        /**
         * @brief 异步传输是否完成
         *
         * @return true
         * @return false
         */
        bool done() {
            return !_buf.reading();
        }


        /**
         * @brief 浅复位，只复位对象内部状态，不影响硬件外设
         *
         */
        void shallow_reset() {
            _buf.reset();
            _tx_ed = false;
            _pending_start_signal = false;
            _last_frame_met_error = false;
        }
    };

}  // namespace twixx